#pragma once
#include "muduo/proto/codec.h"
#include "muduo/proto/dispatcher.h"
#include "muduo/base/Logging.h"
#include "muduo/base/Mutex.h"
#include "muduo/net/EventLoop.h"
#include "muduo/net/TcpServer.h"
#include "../mqcommon/mq_logger.hpp"
#include "../mqcommon/mq_msg.pb.h"
#include "../mqcommon/mq_proto.pb.h"
#include "mq_virhost.hpp"
#include "mq_threadpool.hpp"
#include "mq_connection.hpp"
#include "mq_consumer.hpp"

namespace mymq{
    #define DBFILE "/meta.db"
    #define HOSTNAME "MyVirtualHost"
    class Server {
        public:
            typedef std::shared_ptr<google::protobuf::Message> MessagePtr;
            Server(int port,const std::string &basedir): _server(&_baseloop, muduo::net::InetAddress("0.0.0.0", port), 
                "Server", muduo::net::TcpServer::kReusePort),
                _dispatcher(std::bind(&Server::onUnknownMessage, this, std::placeholders::_1, 
                    std::placeholders::_2, std::placeholders::_3)),
                _codec(std::make_shared<ProtobufCodec>(std::bind(&ProtobufDispatcher::onProtobufMessage, &_dispatcher, 
                    std::placeholders::_1, std::placeholders::_2, std::placeholders::_3))),
                _consumer_manager(std::make_shared<ConsumerManager>()),
                _connect_manager(std::make_shared<ConnectionManager>()),
                _host(std::make_shared<VirtualHost>(HOSTNAME,basedir,basedir+DBFILE)),
                _pool(std::make_shared<Threadpool>()){
                //在初始化队列时也需要将每个队列的消费者也初始化
                //获取所有的队列
                QueueMap mq = _host->AllQueues();
                for(auto &q:mq)
                {
                    _consumer_manager->InitQueueConsumer(q.first);
                }
                //注册业务请求处理函数
                _dispatcher.registerMessageCallback<mymq::openChannelRequest>(std::bind(&Server::onOpenChannel, this, 
                    std::placeholders::_1, std::placeholders::_2, std::placeholders::_3));
                _dispatcher.registerMessageCallback<mymq::closeChannelRequest>(std::bind(&Server::onCloseChannel, this, 
                    std::placeholders::_1, std::placeholders::_2, std::placeholders::_3));
                _dispatcher.registerMessageCallback<mymq::declareExchangeRequest>(std::bind(&Server::onDeclareExchange, this, 
                    std::placeholders::_1, std::placeholders::_2, std::placeholders::_3));
                _dispatcher.registerMessageCallback<mymq::deleteExchangeRequest>(std::bind(&Server::onDeleteExchange, this, 
                    std::placeholders::_1, std::placeholders::_2, std::placeholders::_3));
                _dispatcher.registerMessageCallback<mymq::declareQueueRequest>(std::bind(&Server::onDeclareQueue, this, 
                    std::placeholders::_1, std::placeholders::_2, std::placeholders::_3));
                _dispatcher.registerMessageCallback<mymq::deleteQueueRequest>(std::bind(&Server::onDeleteQueue, this, 
                    std::placeholders::_1, std::placeholders::_2, std::placeholders::_3));
                _dispatcher.registerMessageCallback<mymq::queueBindRequest>(std::bind(&Server::onQueueBind, this, 
                    std::placeholders::_1, std::placeholders::_2, std::placeholders::_3));
                _dispatcher.registerMessageCallback<mymq::queueUnBindRequest>(std::bind(&Server::onQueueUnBind, this, 
                    std::placeholders::_1, std::placeholders::_2, std::placeholders::_3));
                _dispatcher.registerMessageCallback<mymq::basicPublishRequest>(std::bind(&Server::onBasicPublish, this, 
                    std::placeholders::_1, std::placeholders::_2, std::placeholders::_3));
                _dispatcher.registerMessageCallback<mymq::basicAckRequest>(std::bind(&Server::onBasicAck, this, 
                    std::placeholders::_1, std::placeholders::_2, std::placeholders::_3));
                _dispatcher.registerMessageCallback<mymq::basicConsumeRequest>(std::bind(&Server::onBasicConsume, this, 
                    std::placeholders::_1, std::placeholders::_2, std::placeholders::_3));
                _dispatcher.registerMessageCallback<mymq::basicCancelRequest>(std::bind(&Server::onBasicCancel, this, 
                    std::placeholders::_1, std::placeholders::_2, std::placeholders::_3));

                _server.setMessageCallback(std::bind(&ProtobufCodec::onMessage, _codec.get(),
                    std::placeholders::_1, std::placeholders::_2, std::placeholders::_3));
                _server.setConnectionCallback(std::bind(&Server::onConnection, this, std::placeholders::_1));
            }
            void Start() {
                _server.start();
                _baseloop.loop();
            }
        private:
            //创建信道
            void onOpenChannel(const muduo::net::TcpConnectionPtr& conn, const openChannelRequestPtr& message, muduo::Timestamp) 
            {
                //先查看是否存在连接
                auto my_conn = _connect_manager->GetConnection(conn);
                if(my_conn.get()==nullptr)
                {
                    DLOG("打开信道时，未找到该连接");
                    conn->shutdown();
                    return;
                }
                //创建信道
                return my_conn->OpenChannel(message);
            }
            //删除信道
            void onCloseChannel(const muduo::net::TcpConnectionPtr& conn, const closeChannelRequestPtr& message, muduo::Timestamp) 
            {
                //先查看是否存在连接
                auto my_conn = _connect_manager->GetConnection(conn);
                if(my_conn.get()==nullptr)
                {
                    DLOG("关闭信道时，未找到该连接");
                    conn->shutdown();
                    return;
                }
                return my_conn->CloseChannel(message);
            }
            //声明交换机
            void onDeclareExchange(const muduo::net::TcpConnectionPtr& conn, const declareExchangeRequestPtr& message, muduo::Timestamp) 
            {
                //先查看是否存在连接
                auto my_conn = _connect_manager->GetConnection(conn);
                if(my_conn.get()==nullptr)
                {
                    DLOG("声明交换机时，未找到该连接");
                    conn->shutdown();
                    return;
                }
                auto cp = my_conn->GetChannel(message->cid());
                if(cp.get()==nullptr)
                {
                    DLOG("声明交换机时，未找到信道");
                    return;
                }
                return cp->DectoryExchange(message);
            }
            //删除交换机
            void onDeleteExchange(const muduo::net::TcpConnectionPtr& conn, const deleteExchangeRequestPtr& message, muduo::Timestamp) 
            {
                //先查看是否存在连接
                auto my_conn = _connect_manager->GetConnection(conn);
                if(my_conn.get()==nullptr)
                {
                    DLOG("删除交换机时，未找到该连接");
                    conn->shutdown();
                    return;
                }
                auto cp = my_conn->GetChannel(message->cid());
                if(cp.get()==nullptr)
                {
                    DLOG("删除交换机时，未找到信道");
                    return;
                }
                return cp->DeleteExchange(message);
            }
            //声明队列
            void onDeclareQueue(const muduo::net::TcpConnectionPtr& conn, const declareQueueRequestPtr& message, muduo::Timestamp) 
            {
                //先查看是否存在连接
                auto my_conn = _connect_manager->GetConnection(conn);
                if(my_conn.get()==nullptr)
                {
                    DLOG("声明队列时，未找到该连接");
                    conn->shutdown();
                    return;
                }
                auto cp = my_conn->GetChannel(message->cid());
                if(cp.get()==nullptr)
                {
                    DLOG("声明队列时，未找到信道");
                    return;
                }
                return cp->DeclareQueue(message);
            }
            //删除队列
            void onDeleteQueue(const muduo::net::TcpConnectionPtr& conn, const deleteQueueRequestPtr& message, muduo::Timestamp) 
            {
                //先查看是否存在连接
                auto my_conn = _connect_manager->GetConnection(conn);
                if(my_conn.get()==nullptr)
                {
                    DLOG("删除队列时，未找到该连接");
                    conn->shutdown();
                    return;
                }
                auto cp = my_conn->GetChannel(message->cid());
                if(cp.get()==nullptr)
                {
                    DLOG("删除队列时，未找到信道");
                    return;
                }
                return cp->DeleteQueue(message);
            }
            //进行队列绑定
            void onQueueBind(const muduo::net::TcpConnectionPtr& conn, const queueBindRequestPtr& message, muduo::Timestamp) 
            {
                //先查看是否存在连接
                auto my_conn = _connect_manager->GetConnection(conn);
                if(my_conn.get()==nullptr)
                {
                    DLOG("进行绑定时，未找到该连接");
                    conn->shutdown();
                    return;
                }
                auto cp = my_conn->GetChannel(message->cid());
                if(cp.get()==nullptr)
                {
                    DLOG("进行绑定时，未找到信道");
                    return;
                }
                return cp->QueueBind(message);
            }
            //解除绑定
            void onQueueUnBind(const muduo::net::TcpConnectionPtr& conn, const queueUnBindRequestPtr& message, muduo::Timestamp) 
            {
                //先查看是否存在连接
                auto my_conn = _connect_manager->GetConnection(conn);
                if(my_conn.get()==nullptr)
                {
                    DLOG("进行解除绑定时，未找到该连接");
                    conn->shutdown();
                    return;
                }
                auto cp = my_conn->GetChannel(message->cid());
                if(cp.get()==nullptr)
                {
                    DLOG("进行解除绑定时，未找到信道");
                    return;
                }
                return cp->QueueUnBind(message);
            }
            //消息发布
            void onBasicPublish(const muduo::net::TcpConnectionPtr& conn, const basicPublishRequestPtr& message, muduo::Timestamp) 
            {
                //先查看是否存在连接
                auto my_conn = _connect_manager->GetConnection(conn);
                if(my_conn.get()==nullptr)
                {
                    DLOG("进行消息发布时，未找到该连接");
                    conn->shutdown();
                    return;
                }
                auto cp = my_conn->GetChannel(message->cid());
                if(cp.get()==nullptr)
                {
                    DLOG("进行消息发布时，未找到信道");
                    return;
                }
                return cp->BasicPublish(message);
            }
            //消息订阅
            void onBasicConsume(const muduo::net::TcpConnectionPtr& conn, const basicConsumeRequestPtr& message, muduo::Timestamp) 
            {
                //先查看是否存在连接
                auto my_conn = _connect_manager->GetConnection(conn);
                if(my_conn.get()==nullptr)
                {
                    DLOG("进行消息订阅时，未找到该连接");
                    conn->shutdown();
                    return;
                }
                auto cp = my_conn->GetChannel(message->cid());
                if(cp.get()==nullptr)
                {
                    DLOG("进行消息订阅时，未找到信道");
                    return;
                }
                return cp->BasicConsume(message);
            }
            //取消订阅
            void onBasicCancel(const muduo::net::TcpConnectionPtr& conn, const basicCancelRequestPtr& message, muduo::Timestamp) 
            {
                //先查看是否存在连接
                auto my_conn = _connect_manager->GetConnection(conn);
                if(my_conn.get()==nullptr)
                {
                    DLOG("进行取消消息订阅时，未找到该连接");
                    conn->shutdown();
                    return;
                }
                auto cp = my_conn->GetChannel(message->cid());
                if(cp.get()==nullptr)
                {
                    DLOG("进行取消消息订阅时，未找到信道");
                    return;
                }
                return cp->BasicCancel(message);
            }
            //消息确认
            void onBasicAck(const muduo::net::TcpConnectionPtr& conn, const basicAckRequestPtr& message, muduo::Timestamp) 
            {
                //先查看是否存在连接
                auto my_conn = _connect_manager->GetConnection(conn);
                if(my_conn.get()==nullptr)
                {
                    DLOG("进行消息确认时，未找到该连接");
                    conn->shutdown();
                    return;
                }
                auto cp = my_conn->GetChannel(message->cid());
                if(cp.get()==nullptr)
                {
                    DLOG("进行消息确认时，未找到信道");
                    return;
                }
                return cp->BasicAck(message);
            }
            
            void onUnknownMessage(const muduo::net::TcpConnectionPtr& conn, const MessagePtr& message, muduo::Timestamp) {
                LOG_INFO << "onUnknownMessage: " << message->GetTypeName();
                conn->shutdown();
            }
            void onConnection(const muduo::net::TcpConnectionPtr &conn) {
                if (conn->connected()) {
                    LOG_INFO << "新连接建立成功！";
                    //将连接添加到管理句柄中
                    _connect_manager->AddConnection(conn,_codec,_consumer_manager,_host,_pool);
                }else {
                    LOG_INFO << "连接即将关闭！";
                    //将连接从管理中删除
                    _connect_manager->DeleteConnection(conn);
                }
            }
        private:
            muduo::net::EventLoop _baseloop;
            muduo::net::TcpServer _server;//服务器对象
            ProtobufDispatcher _dispatcher;//请求分发器对象--要向其中注册请求处理函数
            ProtobufCodecPtr _codec;//protobuf协议处理器--针对收到的请求数据进行protobuf协议处理
            ConsumerManager::Ptr _consumer_manager;//消费者管理
            ConnectionManager::Ptr _connect_manager;//连接管理
            VirtualHost::Ptr _host;
            Threadpool::Ptr _pool;
    };
}